package com.gun3y.ts.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import virtuoso.jena.driver.VirtBulkUpdateHandler;
import virtuoso.jena.driver.VirtGraph;
import virtuoso.jena.driver.VirtuosoQueryExecution;
import virtuoso.jena.driver.VirtuosoQueryExecutionFactory;

import com.gun3y.exception.ExceptionCode;
import com.gun3y.exception.SWException;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.AnonId;
import com.hp.hpl.jena.rdf.model.InfModel;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.reasoner.rulesys.GenericRuleReasoner;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

public class TripleStore {

    private String db = "jdbc:virtuoso://localhost:1111";

    private String userName = "dba";

    private String password = "dba";

    private VirtGraph graph;

    // public static void main(String[] args) throws IOException {
    // TripleStore store = new TripleStore();
    // Model createModelForGraph =
    // ModelFactory.createModelForGraph(store.graph);
    //
    // RDFWriter writer = createModelForGraph.getWriter();
    //
    // writer.setProperty("showXmlDeclaration", "true");
    // writer.setProperty("tab", "8");
    // writer.setProperty("relativeURIs", "same-document,relative");
    //
    // StringWriter stringWriter = new StringWriter();
    // writer.write(createModelForGraph, stringWriter,
    // "http://www.gun3y.com/ontology/movie");
    // stringWriter.close();
    // System.out.println(stringWriter.toString());
    //
    // }

    public TripleStore(String graphName) {
	this.graph = new VirtGraph(graphName, db, userName, password);
    }

    public void clear() {
	this.graph.clear();
    }

    public ExtendedIterator<Triple> execute(Node s, Node p, Node o) {
	Node s1 = s == null ? Node.ANY : s;
	Node p1 = p == null ? Node.ANY : p;
	Node o1 = o == null ? Node.ANY : o;

	return this.graph.find(s1, p1, o1);
    }

    public boolean contain(Node s, Node p, Node o) {
	Node s1 = s == null ? Node.ANY : s;
	Node p1 = p == null ? Node.ANY : p;
	Node o1 = o == null ? Node.ANY : o;

	return this.graph.contains(s1, p1, o1);
    }

    public void addTriple(Node s, Node p, Node o) {
	this.graph.add(new Triple(s, p, o));
    }

    public void removeTriple(List<Triple> triples) {
	this.graph.remove(triples);
    }

    public void addTriplesAsBulk(Collection<Triple> triples) {
	if (triples == null || triples.isEmpty())
	    return;

	VirtBulkUpdateHandler bulkUpdateHandler = new VirtBulkUpdateHandler(this.graph);
	bulkUpdateHandler.add(triples.toArray(new Triple[triples.size()]));
    }

    public List<QuerySolution> executeSelectQuery(String query) {
	List<QuerySolution> retList = new ArrayList<QuerySolution>();

	if (StringUtils.isBlank(query)) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}
	try {
	    Query sparql = QueryFactory.create(query);

	    if (query != null) {
		VirtuosoQueryExecution vqe = VirtuosoQueryExecutionFactory.create(sparql, this.graph);

		ResultSet results = vqe.execSelect();
		while (results.hasNext()) {
		    QuerySolution result = results.nextSolution();
		    retList.add(result);
		}
	    }
	    return retList;
	}
	catch (Exception ex) {
	    ex.printStackTrace();
	    throw new SWException(ExceptionCode.UnknownError);
	}
    }

    public boolean executeAskQuery(String query) {
	if (StringUtils.isBlank(query)) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}
	try {
	    Query sparql = QueryFactory.create(query);

	    VirtuosoQueryExecution vqe = VirtuosoQueryExecutionFactory.create(sparql, this.graph);

	    return vqe.execAsk();
	}
	catch (Exception ex) {
	    ex.printStackTrace();
	    throw new SWException(ExceptionCode.UnknownError);
	}
    }

    public AnonId executeAskAnonId(String query) {
	if (StringUtils.isBlank(query)) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}

	try {
	    Query sparql = QueryFactory.create(query);

	    VirtuosoQueryExecution vqe = VirtuosoQueryExecutionFactory.create(sparql, this.graph);

	    ResultSet results = vqe.execSelect();
	    while (results.hasNext()) {
		QuerySolution result = results.nextSolution();
		RDFNode node = result.get("node");
		if (node.isAnon()) {
		    return node.asResource().getId();
		}
	    }
	}
	catch (Exception ex) {
	    ex.printStackTrace();
	    throw new SWException(ExceptionCode.UnknownError);
	}

	return null;
    }

    @Override
    public String toString() {
	StringBuilder builder = new StringBuilder();
	ExtendedIterator<Triple> iter = this.graph.find(Node.ANY, Node.ANY, Node.ANY);
	for (; iter.hasNext();) {
	    builder.append(iter.next().toString()).append("\n");
	}

	return builder.toString();
    }

    public void executeReasoner(GenericRuleReasoner reasoner) {
	Model model = ModelFactory.createModelForGraph(this.graph);
	InfModel infmodel = ModelFactory.createInfModel(reasoner, model);
	model.add(infmodel.getDeductionsModel());
    }
}
